/*
 * Copyright (c) 2012-2017 Red Hat, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Red Hat, Inc. - initial API and implementation
 */
package org.eclipse.che.plugin.openshift.client.kubernetes;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;

public final class KubernetesStringUtils {

  /** Max length of a Kubernetes name or label; */
  private static final int MAX_CHARS = 63;

  private static final String DOCKER_PREFIX = "docker://";

  private KubernetesStringUtils() {}

  /**
   * Converts strings to fit requirements of Kubernetes names and labels. Names in Kubernetes are
   * limited to 63 characters.
   *
   * @param input the string to normalize
   * @return the normalized string.
   */
  public static String getNormalizedString(String input) {
    int end = Math.min(input.length(), MAX_CHARS - 1);
    String stringLessThan63chars = input.substring(0, end);
    String normalizedString = removeSpecialCharactersInTheEnd(stringLessThan63chars);
    return normalizedString;
  }

  /**
   * @param containerID
   * @return normalized version of 'ContainerID' without 'docker://' prefix and double quotes
   */
  public static String normalizeContainerID(final String containerID) {
    return StringUtils.replaceOnce(containerID, DOCKER_PREFIX, "").replace("\"", "");
  }

  /**
   * @param containerID
   * @return label based on 'ContainerID' (first 12 chars of ID)
   */
  public static String getLabelFromContainerID(final String containerID) {
    return StringUtils.substring(containerID, 0, 12);
  }

  /**
   * Che workspace id is used as OpenShift service / deployment config name and must match the regex
   * [a-z]([-a-z0-9]*[a-z0-9]) e.g. "q5iuhkwjvw1w9emg"
   *
   * @return randomly generated workspace id
   */
  public static String generateWorkspaceID() {
    return RandomStringUtils.random(16, true, true).toLowerCase();
  }

  /**
   * Converts a String into a suitable name for an openshift container. Kubernetes names are limited
   * to 63 chars and must match the regex {@code [a-z0-9]([-a-z0-9]*[a-z0-9])?}
   *
   * @param input the string to convert
   */
  public static String convertToContainerName(String input) {
    if (input.startsWith("workspace")) {
      input = input.replaceFirst("workspace", "");
    }
    return getNormalizedString(input.replaceAll("_", "-"));
  }

  /**
   * Converts image stream name (e.g. eclipse/ubuntu_jdk8 to eclipse_ubuntu_jdk8). This has to be
   * done because for OpenShift ImageStream names, the organization component of a docker repository
   * is the namespace of the ImageStream, and so '/' is not supported in ImageStream names.
   *
   * @param repository the original docker repository String.
   * @return
   */
  public static String convertPullSpecToImageStreamName(String repository) {
    repository = stripTagFromPullSpec(repository);
    return getNormalizedString(repository.replaceAll("/", "_"));
  }

  /**
   * Generates a name to be used as a tag from a docker repository. In OpenShift, tagging
   * functionality is limited, so while in Docker we may want to
   *
   * <p>{@code docker tag eclipse/ubuntu_jdk8 eclipse-che/<workspace-id>},
   *
   * <p>this is not possible in OpenShift. This method returns a trimmed version of {@code
   * <workspace-id>}
   *
   * @param repository the target repository spec in a {@code docker tag} command.
   * @return an appropriate tag name
   */
  public static String convertPullSpecToTagName(String repository) {
    String name;
    if (repository.contains("/")) {
      String[] nameSegments = repository.split("/");
      name = nameSegments[nameSegments.length - 1];
    } else {
      name = repository;
    }
    name = stripTagFromPullSpec(name);
    name =
        name.replaceAll("workspace", "")
            .replaceAll("machine", "")
            .replaceAll("che_.*", "")
            .replaceAll("_", "");

    return getNormalizedString(name);
  }

  /**
   * Gets an ImageStreamTag name from docker pull specs by converting repository strings to suit the
   * convention used in {@link KubernetesStringUtils#convertPullSpecToImageStreamName(String)} and
   * {@link KubernetesStringUtils#convertPullSpecToTagName(String)}.
   *
   * <p>e.g. will convert {@code eclipse/ubuntu_jdk8} and {@code eclipse-che/<workspace-id>} into
   * {@code eclipse_ubuntu_jdk8:<workspace-id>}
   *
   * @param oldRepository The docker image repository that is tracked by the ImageStream
   * @param newRepository The docker repository that has been tagged to follow oldRepository
   * @return A string that can be used to refer to the ImageStreamTag formed from these
   *     repositories.
   */
  public static String createImageStreamTagName(String oldRepository, String newRepository) {
    String tag = convertPullSpecToTagName(newRepository);
    String repo = convertPullSpecToImageStreamName(oldRepository);
    return getNormalizedString(String.format("%s:%s", repo, tag));
  }

  /**
   * Gets the ImageStreamName fromm a docker pull spec. For example, provided {@code
   * [<registry>]/[<organization>]/<image>:[<tag>]}, will return just {@code <image>}
   *
   * <p>In the case where the pull spec does not contain optional components, this method simply
   * returns the pull spec provided.
   *
   * @param pullSpec
   * @return
   */
  public static String getImageStreamNameFromPullSpec(String pullSpec) {
    return pullSpec.replaceAll(".*/", "").replaceAll(":.*", "");
  }

  /**
   * Remove the tag from a pull spec, if applicable. If pull spec does not include a tag, returns
   * the pull spec unchanged.
   *
   * @param pullSpec
   * @return
   */
  public static String stripTagFromPullSpec(String pullSpec) {
    return pullSpec.replaceAll(":.*", "");
  }

  /**
   * Gets the tag fromm a docker pull spec. For example, provided {@code
   * [<registry>]/[<organization>]/<image>:[<tag>]}, will return just {@code <tag>}
   *
   * @param pullSpec
   * @return the tag on the pull spec, or null if pull spec does not contain a tag
   */
  public static String getTagNameFromPullSpec(String pullSpec) {
    if (!pullSpec.contains(":")) {
      return null;
    }
    return pullSpec.replaceAll(".*:", "");
  }

  /** Removes non-word-characters at the end of the string */
  private static String removeSpecialCharactersInTheEnd(final String input) {
    return input.replaceAll("\\W+$", "");
  }
}
